/*
 *  de.karnik.helper - Library for basic helper functions
 *  Copyright (C) 2007-2008 Markus Karnik
 *
 *  This program is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 3 of the License, 
 *  or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along 
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
 *  
 */

package de.karnik.helper;

import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * Useful functions to handle, convert or create hashes.
 * 
 * @author Markus Karnik - markus.karnik@my-design.org
 * @version 1.0
 * @since 1.0
 */
public class HashFunctions {

	/**
	 * Calculates an returns the HmacSha1-Hash of the given data-key-combination.
	 * 
	 * @param key The key for the HmacSha1-calculation.
	 * @param data The data for the HmacSha1-calculation.
	 * @return The calculated HmacSha1-Hash.
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 */
	public static byte[] hmacSha1( byte[] key, byte[] data ) throws NoSuchAlgorithmException, InvalidKeyException {
		return hmacSha1( 0, 20, key, data );
	}

	
	/**
	 * Calculates the HmacSha1-Hash of the given data-key-combination 
	 * and returns the part of the hash which is specified by offset and length.
	 * 
	 * @param offset The offset of the extracted piece.
	 * @param length The length of the extracted piece.
	 * @param key The key for the HmacSha1-calculation.
	 * @param data The data for the HmacSha1-calculation.
	 * @return The extracted piece of the calculated HmacSha1-Hash.
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 */
	public static byte[] hmacSha1( int offset, int length, byte[] key, byte[] data ) throws NoSuchAlgorithmException, InvalidKeyException {
		Mac hmacSha1 = Mac.getInstance( "HmacSHA1" );
		
		SecretKeySpec  spec  = new SecretKeySpec( key, "HmacSHA1" );
		hmacSha1.init( spec );
		
		byte[] temp = hmacSha1.doFinal( data );
		
		return ArrayFunctions.seperateDataFromArray( offset, length, temp );
	}
	
	/**
	 * Calculates the HmacSha256-Hash of the given data-key-combination 
	 * and returns the first 160 bit of the calculated hash.
	 * 
	 * @param key The key for the HmacSha256-calculation.
	 * @param data The data for the HmacSha256-calculation.
	 * @return The first 160 bit of the calculated HmacSha256-Hash.
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 */
	public static byte[] hmacSha256160( byte[] key, byte[] data ) throws NoSuchAlgorithmException, InvalidKeyException {
		return hmacSha256( 0, 20, key, data );
	}
	
	/**
	 * Calculates an returns the HmacSha256-Hash of the given data-key-combination.
	 * 
	 * @param key The key for the HmacSha256-calculation.
	 * @param data The data for the HmacSha256-calculation.
	 * @return The calculated HmacSha256-Hash.
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 */
	public static byte[] hmacSha256( byte[] key, byte[] data ) throws NoSuchAlgorithmException, InvalidKeyException {
		return hmacSha256( 0, 32, key, data );
	}
	
	/**
	 * Calculates the HmacSha256-Hash of the given data-key-combination 
	 * and returns the part of the hash which is specified by offset and length.
	 * 
	 * @param offset The offset of the extracted piece.
	 * @param length The length of the extracted piece.
	 * @param key The key for the HmacSha256-calculation.
	 * @param data The data for the HmacSha256-calculation.
	 * @return The extracted piece of the calculated HmacSha256-Hash.
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 */
	public static byte[] hmacSha256( int offset, int length, byte[] key, byte[] data ) throws NoSuchAlgorithmException, InvalidKeyException {
		Mac hmacSha256 = Mac.getInstance( "HmacSHA256" );
		
		SecretKeySpec  spec  = new SecretKeySpec( key, "HmacSHA256" );
		hmacSha256.init( spec );
		
		byte[] temp = hmacSha256.doFinal( data );
		
		return ArrayFunctions.seperateDataFromArray( offset, length, temp );
	}
	
	/**
	 * Calculates the md5 hash of the given string.
	 * 
	 * @param string The string to generate the md5 hash from. 
	 * @return The md5 hash of the given string.
	 */
	public static String md5( String string ) {
		MessageDigest md5 = null;
		byte[] digest; 
		StringBuffer strbuf = new StringBuffer();
		
		try {
			md5 = MessageDigest.getInstance( "MD5" );
		} catch ( NoSuchAlgorithmException e ) {
			LogFunctions.log( "HashFunctions.md5(String)" , "Exception", e.getMessage());
			LogFunctions.log( "HashFunctions.md5(String)" , "Exception", e.getStackTrace() );
		}
		
		
        md5.update( string.getBytes(), 0, string.length() );     
        digest = md5.digest();

        for (int i = 0; i < digest.length; i++)
           strbuf.append( StringFunctions.toHexString( digest[i] ) );
    
		
		return strbuf.toString().toLowerCase();
	}
	
	/**
	 * Calculates the SHA256 hash of the given string.
	 * 
	 * @param string The string to generate the SHA256 hash from. 
	 * @return The SHA256 hash of the given string.
	 */
	public static String sha256( String string ) {
		MessageDigest sha256 = null;
		byte[] digest; 
		StringBuffer strbuf = new StringBuffer();
		
		try {
			sha256 = MessageDigest.getInstance( "SHA256" );
		} catch ( NoSuchAlgorithmException e ) {
			LogFunctions.log( "HashFunctions.sha256(String)" , "Exception", e.getMessage());
			LogFunctions.log( "HashFunctions.sha256(String)" , "Exception", e.getStackTrace() );
		}
		
		
		sha256.update( string.getBytes(), 0, string.length() );     
        digest = sha256.digest();

        for (int i = 0; i < digest.length; i++)
           strbuf.append( StringFunctions.toHexString( digest[ i ] ) );
    
		
		return strbuf.toString().toLowerCase();
	}
	
	/**
	 * Calculates the SHA256 hash of the given data.
	 * 
	 * @param data The byte[] to generate the SHA256 hash from. 
	 * @return The SHA256 hash as byte[] of the given string.
	 */
	public static byte[] sha256( byte[] data ) {
		MessageDigest sha256 = null;
		byte[] digest; 
		
		try {
			sha256 = MessageDigest.getInstance( "SHA256" );
		} catch ( NoSuchAlgorithmException e ) {
			LogFunctions.log( "HashFunctions.sha256(byte[])" , "Exception", e.getMessage());
			LogFunctions.log( "HashFunctions.sha256(byte[])" , "Exception", e.getStackTrace() );
		}
		
		sha256.update( data, 0, data.length );
        digest = sha256.digest();
		
		return digest;
	}
	
	/**
	 * Calculates the SHA1hash of the given data.
	 * 
	 * @param data The byte[] to generate the SHA1 hash from. 
	 * @return The SHA1 hash as byte[] of the given string.
	 */
	public static byte[] sha1( byte[] data ) {
		MessageDigest sha1 = null;
		
		try {
			sha1 = MessageDigest.getInstance( "SHA-1" );
			return sha1.digest( data );
		} catch ( NoSuchAlgorithmException e ) { 
			LogFunctions.log( "HashFunctions.sha1" , "Exception", e.getMessage());
			LogFunctions.log( "HashFunctions.sha1" , "Exception", e.getStackTrace() );
		}

		return null;
	}
	
}
